package zhongbo.javaweb.instagram.schedule;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import zhongbo.javaweb.instagram.enums.InsUrl;
import zhongbo.javaweb.instagram.enums.ServiceType;
import zhongbo.javaweb.instagram.service.ProcessService;
import zhongbo.javaweb.instagram.util.DateFormatUtil;
import zhongbo.javaweb.instagram.util.SysUtil;
import zhongbo.javaweb.instagram.web.util.RestTemplateUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 说明：
 *
 * @author zhongboWork
 * @version 1.0
 * @see <a href="https://blog.csdn.net/qq_34129814/article/details/85090162">@Scheduled中fixedDelay、fixedRate、initialDelay 和cron表达式的解析及区别</a>
 * @since 2022-01-18 17:09
 */
@Component
@Slf4j
@Data
public class FileSchedule {

    @Autowired
    private RestTemplateUtils restTemplateUtils;
    @Autowired
    private ProcessService processService;

    @Value("${service.type}")
    private ServiceType serviceType;
    @Value("${service.middle.url}")
    private String middleUrl;
    @Value("${file.saveDir}")
    private String saveDir;
    @Value("${file.enable}")
    private boolean enable;

    @Value("${file.check.intervalMs}")
    private long fileCheckIntervalMs = 60000;


    private long lastCheckTime;

    private AtomicLong atomicLong = new AtomicLong(0);


    @Scheduled(fixedDelay = 1000)
    public void checkHeartbeat() {
        if (System.currentTimeMillis() - lastCheckTime > fileCheckIntervalMs) {
            if(!enable) {
                return;
            }
            lastCheckTime = System.currentTimeMillis();
            if (serviceType == ServiceType.file || serviceType == ServiceType.one) {
                log.info("======== 客户端 [图片] 同步, 间隔配置 {}", DateFormatUtil.formatMs(fileCheckIntervalMs));
                if (SysUtil.isWindows()) {
                    log.error("客户端 [图片] 同步失败! 未在linux系统下运行");
                    return;
                }
                syncLastFile(middleUrl);
            }
        }
    }

    private void syncLastFile(String middleUrl) {
        ResponseEntity<String> responseEntity = restTemplateUtils.get(middleUrl + InsUrl.LAST_IMAGE_FILE, String.class);
        long past = System.currentTimeMillis() - lastCheckTime;
        if (responseEntity.getStatusCodeValue() == InsUrl.SUCCESS) {
            String data = responseEntity.getBody();
            log.warn("获取图片时间! past {}", DateFormatUtil.formatMs(past));
            List<String> fileList = extractFileList(data);
            if (fileList == null) {
                log.warn("解析图片失败! data {}", data);
                return;
            }
            if (fileList.isEmpty()) {
                log.warn("同步图片完成! 没有新的图片 {} 已同步 {}", fileList, atomicLong.get());
                return;
            }
            downloadFiles(fileList);
        } else {
            log.warn("获取图片失败! past {}", DateFormatUtil.formatMs(past));
        }
    }

    private void downloadFiles(List<String> fileList) {
        File saveFileDir = new File(saveDir);
        if (!saveFileDir.exists()) {
            boolean mkdirs = saveFileDir.mkdirs();
            if (!mkdirs) {
                log.error("下载图片失败! 无法创建下载文件夹 {}; 绝对路径: {}", saveDir, saveFileDir.getAbsolutePath());
                return;
            }
        }
        if (!saveFileDir.isDirectory()) {
            log.error("下载图片失败! 下载文件夹不是一个文件夹 {}; 绝对路径: {}", saveDir, saveFileDir.getAbsolutePath());
            return;
        }
        for (String file : fileList) {
            long start = System.currentTimeMillis();
            try {
                String getUrl = middleUrl + InsUrl.IMAGE_GET_FILE + file;
                log.info("====== 开始同步图片: {}", file);
                String[] split = file.split("-");
                StringBuilder sb = new StringBuilder(saveFileDir.getAbsolutePath());
                for (String s : split) {
                    if (!"data".equals(s)) {
                        sb.append(File.separator).append(s);
                    }
                }
                File outPutFile = new File(sb.toString());
                if (outPutFile.exists()) {
                    deleteRemoteFile(file);
                    long past = System.currentTimeMillis() - start;
                    log.info("同步图片成功! 下载图片已存在, 跳过下载 {}, 耗时: {}, 已同步 {} 条", outPutFile.getAbsolutePath(), DateFormatUtil.formatMs(past), atomicLong.incrementAndGet());
                } else {
                    ResponseEntity<byte[]> responseEntity = restTemplateUtils.get(getUrl, byte[].class);
                    if (responseEntity.getStatusCodeValue() == 200) {
                        byte[] body = responseEntity.getBody();
                        if (body == null) {
                            log.error("下载文件失败, 下载内容为空! body is null; file url: {}", getUrl);
                            return;
                        }
                        boolean mkdirs = outPutFile.getParentFile().mkdirs();
                        if (mkdirs) {
                            log.info("创建文件夹: {}", outPutFile.getParentFile().getAbsolutePath());
                        }
                        if (!outPutFile.createNewFile()) {
                            log.error("下载图片失败, 没有权限创建文件 {}", outPutFile.getAbsolutePath());
                            continue;
                        }
                        FileOutputStream fos = new FileOutputStream(outPutFile);
                        fos.write(body);
                        fos.flush();
                        fos.close();
                        log.info("写入文件 {}, length: {}", outPutFile.getAbsolutePath(), body.length);

                        deleteRemoteFile(file);
                        long past = System.currentTimeMillis() - start;
                        log.info("同步图片成功! {}, length: {}, 耗时: {}, 已同步 {} 条", outPutFile.getAbsolutePath(), body.length, DateFormatUtil.formatMs(past), atomicLong.incrementAndGet());
                    } else {
                        log.error("下载图片失败, 下载状态码错误! status: {}", responseEntity.getStatusCodeValue());
                    }
                }
            } catch (Exception e) {
                log.error("下载图片失败! file: {}", file, e);
            }
        }
    }

    private void deleteRemoteFile(String file) {
        if (StringUtils.isEmpty(file)) {
            log.error("删除远端图片失败! 错误的参数! file: {}", file);
            return;
        }
        String delUrl = middleUrl + InsUrl.IMAGE_DELETE_FILE + file;
        ResponseEntity<String> responseEntity = restTemplateUtils.get(delUrl, String.class);
        if (responseEntity.getStatusCodeValue() == InsUrl.SUCCESS) {
            log.info("删除远端图片成功! file: {}, code: {}", file, responseEntity.getStatusCodeValue());
        } else {
            log.info("删除远端图片失败! file: {}, code: {}", file, responseEntity.getStatusCodeValue());
        }
    }

    private List<String> extractFileList(String data) {
        List<String> fileList = new ArrayList<>();
        if (StringUtils.isEmpty(data)) {
            return null;
        }
        Document document = Jsoup.parse(data);
        Elements li = document.getElementsByTag("li");
        if (li.size() > 0) {
            for (Element element : li) {
                Elements a = element.getElementsByTag("a");
                if (a.size() == 2) {
                    String text = a.get(0).text();
                    if (text.startsWith("data") && text.contains("-") && text.split("-").length == 3) {
                        fileList.add(text);
                    } else {
                        log.debug("剔除无效图片 {}", text);
                    }
                } else {
                    log.error("解析到错误的图片数据: {}", element);
                }
            }
        }
        return fileList;
    }

    public static void main(String[] args) {
        System.out.println("FileSchedule");
        try {
            Connection.Response execute = Jsoup.connect("http://e.7mo.org:8068/file").execute();
            Document document = execute.parse();
            System.out.println("document downloaded");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
